/**
 * 
 */
package jvs.peer.xmpp;

import jvs.peer.io.File;
import jvs.peer.io.FileTransfer;
import jvs.peer.util.Log;

import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smackx.filetransfer.FileTransferListener;
import org.jivesoftware.smackx.filetransfer.FileTransferManager;
import org.jivesoftware.smackx.filetransfer.FileTransferRequest;
import org.jivesoftware.smackx.filetransfer.IncomingFileTransfer;
import org.jivesoftware.smackx.filetransfer.OutgoingFileTransfer;

/**
 * @author qiangli
 * 
 */
public class XmppFileTransfer implements FileTransfer {

	public class MyFileTransferListener implements FileTransferListener,
			Monitor {
		private Exception exception;
		private File infile;
		private IncomingFileTransfer xfer;
		private String pid;

		public MyFileTransferListener(String peer, File infile) {
			this.pid = StringUtils.parseBareAddress(peer);
			this.infile = infile;
		}

		public void cancel() {
			if (xfer != null) {
				xfer.cancel();
			}
			cleanup();
		}

		private void cleanup() {
			try {
				infile = null;
				ftm.removeFileTransferListener(this);
			} catch (RuntimeException e) {
				e.printStackTrace();
			}
		}

		public void fileTransferRequest(final FileTransferRequest req) {
			if (Log.DEBUG) {
				Log.log(this,
						"requestor: " + req.getRequestor() + " filename: "
								+ req.getFileName() + "  desc: "
								+ req.getDescription() + " stream Id: "
								+ req.getStreamID());
			}

			try {
				String from = StringUtils.parseBareAddress(req.getRequestor());
				if (from.equals(pid) == false) {
					if (Log.DEBUG) {
						Log.log(this, "ignored: peer:" + pid + " from: " + from);
					}
					return;
				}

				if (Log.DEBUG) {
					Log.log(this, "accepting infile: " + infile);
				}

				xfer = req.accept();

				if (Log.DEBUG) {
					Log.log(this,
							"accepted, receiving... name: "
									+ xfer.getFileName() + " path: "
									+ xfer.getFilePath());
				}

				xfer.recieveFile(infile);

				return;
			} catch (Exception e) {
				e.printStackTrace();
				//
				exception = e;
				xferAborted = true;
				cleanup();
			}

		}

		public int getProgress() {
			return (int) (xfer != null ? xfer.getProgress() * 100 : 0);
		}

		public String getStatus() {
			String e = (exception != null) ? exception.getMessage() : "";
			if (xfer != null) {
				return xfer.getStatus().toString() + ". " + e;
			}
			return e;
		}

		public boolean isAborted() {
			return xferAborted;
		}

		public boolean isDone() {
			if (xfer != null && xfer.isDone()) {
				cleanup();
				xferAborted = false;
				return true;
			}
			if (xferAborted) {
				cleanup();
				return true;
			}
			return false;
		}
	}

	private FileTransferManager ftm;

	private boolean xferAborted;

	public XmppFileTransfer(FileTransferManager ftm) {
		this.ftm = ftm;
	}

	public void abort() throws Exception {
		xferAborted = true;
	}

	public Monitor receive(String peer, String desc, File file)
			throws Exception {
		xferAborted = false;
		MyFileTransferListener ftl = new MyFileTransferListener(peer,
				file);
		ftm.addFileTransferListener(ftl);
		return ftl;
	}

	public Monitor send(String peer, String desc, File file) throws Exception {
		xferAborted = false;

		final OutgoingFileTransfer xfer = ftm.createOutgoingFileTransfer(peer);

		xfer.sendFile(file, desc);

		Monitor mon = new Monitor() {

			public void cancel() {
				xfer.cancel();
			}

			public int getProgress() {
				return (int) (xfer != null ? xfer.getProgress() * 100 : 0);
			}

			public String getStatus() {
				return xfer.getStatus().toString();
			}

			public boolean isAborted() {
				return xferAborted;
			}

			public boolean isDone() {
				if (xfer.isDone()) {
					xferAborted = false;
					return true;
				}
				if (xferAborted) {
					xfer.cancel();
					return true;
				}
				return false;
			}
		};

		return mon;
	}
}
